package com.leenmvc.core.base;

import com.leenmvc.core.aop.proxy.ProxyUtil;
import com.leenmvc.core.cache.CacheFactory;
import com.leenmvc.core.dao.communal.Wrapper;
import com.leenmvc.core.dao.mysql.wrapper.QueryWrapper;
import com.leenmvc.core.enums.CacheType;
import com.leenmvc.core.utils.ClassUtil;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.List;

/**
 * 所有Service 的父类
 *
 * @param <T>
 */
public abstract class BaseBusinessDao<T> extends BaseDbHandler {

    /**
     * 链接实体赋值(通过主键)
     *
     * @param all      列表数据
     * @param setField 将要被赋值的对象名字
     * @param getField 目标字段的ID值字段
     */
    public <T> void joinSingle(List<T> all, String setField, String getField, BaseBusinessDao dao) {
        Method setMethod = ClassUtil.getSetMethod(getClassType(), setField);
        Method getMethod = ClassUtil.getGetMethod(getClassType(), getField);
        for (T q : all) {
            try {
                Object o = dao.get(getMethod.invoke(q, null));
                setMethod.invoke(q, o);
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 链接实体列表赋值(通过主键)
     *
     * @param all          初始列表
     * @param setField     要被设置值的字段名称
     * @param getField     凭借当前实体的哪个字段获取关联对象
     * @param outFieldName 关联字段在关联对象实体中的名称
     * @param dao          执行查询的dao句柄
     * @param <T>
     */
    public <T> void joinMulti(List<T> all, String setField, String getField, String outFieldName, BaseBusinessDao dao) {
        Method setMethod = ClassUtil.getSetMethod(getClassType(), setField);
        Method getMethod = ClassUtil.getGetMethod(getClassType(), getField);
        for (T t : all) {
            try {
                QueryWrapper taotiid = dao.getQueryWrapper().eq(outFieldName, getMethod.invoke(t, null));
                List<Object> query = dao.query(taotiid);
                setMethod.invoke(t, query);
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 链接单字段到获取到的实体
     *
     * @param all       数据列表
     * @param setField  将要被设置的字段
     * @param getField  将要从元对象中获取的字段
     * @param fieldName 外联对象的哪个字段值设置到setField
     * @param dao       外联对象查询句柄
     * @param <T>
     */
    public <T> void joinSingleField(List<T> all, String setField, String getField, String fieldName, BaseBusinessDao dao) {
        String primaryField = BaseEntity.getPrimaryFieldName(getClassType());
        joinSingleField(all, setField, getField, primaryField, fieldName, dao);
    }

    /**
     * 链接单字段到获取到的实体
     *
     * @param all       数据列表
     * @param setField  将要被设置的字段
     * @param inField  将要从元对象中获取的字段
     * @param outField  外联对象的筛选条件字段
     * @param fieldName 外联对象的哪个字段作为设置到setField
     * @param dao       外联对象查询句柄
     * @param <T>
     */
    public <T> void joinSingleField(List<T> all, String setField, String inField, String outField, String fieldName, BaseBusinessDao dao) {
        Method getMethod = ClassUtil.getGetMethod(getClassType(), inField);
        Method setMethod = ClassUtil.getSetMethod(getClassType(), setField);
        for (T q : all) {
            try {
                // 如果没有缓存，到数据库查询，并且缓存起来
                Object mapkey = getMethod.invoke(q, null);
                // 主类 (类名 + setField)作为cacheKey 的一个Map（主类外键ID, Name）类型
                Class<?> classType = getRealType(dao);
                String cacheKey = classType + setField;
                String fieldKey = cacheKey + ":" + mapkey;
                Object o = CacheFactory.get(CacheType.SINGLE_FIELD, fieldKey);
                if (o == null) {
                    Wrapper queryWrapper = dao.getQueryWrapper().eq(outField, mapkey).incField(fieldName);
                    Object oneField = dao.getOneField(queryWrapper);
                    setMethod.invoke(q, oneField);
                    CacheFactory.put(CacheType.SINGLE_FIELD, fieldKey, oneField);
                } else {
                    setMethod.invoke(q, o);
                }

            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 获取父类型的泛型信息
     *
     * @return
     */
    // 使用反射技术得到T的真实类型
    public Class getRealType(BaseBusinessDao dao) {
        Object target = null;
        try {
            target = ProxyUtil.getTarget(dao);
        } catch (Exception e) {
            e.printStackTrace();
        }

        Type type = target.getClass().getGenericSuperclass();
        ParameterizedType pt = (ParameterizedType) type;
        Type[] ts = pt.getActualTypeArguments();
        Class c = (Class) ts[0];
        return c;
    }

}
